﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace mediaPrime.Logger
{
    public static class Log
    {
        private static TraceLevel traceLevel = TraceLevel.Off;
        private static bool initialized = false;
        internal static void Initialize(string logFile, TraceLevel level)
        {
            if (initialized)
            {
                Warning("Logger already initialized!");
                return;
            }

            traceLevel = level;

            try
            {
                var loggerFileStream = new FileStream(logFile, FileMode.Append, FileAccess.Write, FileShare.Read);
                var traceListener = new TextWriterTraceListener(loggerFileStream);

                Trace.Listeners.Add(traceListener);
                Trace.AutoFlush = true;

                initialized = true;
            }
            catch (Exception ex)
            {
                // An exception occured which can't be logged, because the logger could not be initialized
                MessageBox.Show("Error while initializing the Logger: " + ex.ToString());
            }
        }

        [ConditionalAttribute("TRACE")]
        public static void Info(string format, params object[] args)
        {
            Info(string.Format(CultureInfo.InvariantCulture, format, args));
        }

        [ConditionalAttribute("TRACE")]
        public static void Info(string message)
        {
            var sender = Assembly.GetCallingAssembly();
            if (traceLevel >= TraceLevel.Info)
                TraceLine(TraceLevel.Info, message, sender);
        }

        [ConditionalAttribute("TRACE")]
        public static void Error(string format, params object[] args)
        {
            Error(string.Format(CultureInfo.InvariantCulture, format, args));
        }

        [ConditionalAttribute("TRACE")]
        public static void Error(string message)
        {
            var sender = Assembly.GetCallingAssembly();
            if (traceLevel >= TraceLevel.Error)
                TraceLine(TraceLevel.Error, message, sender);
        }

        [ConditionalAttribute("TRACE")]
        public static void Warning(string format, params object[] args)
        {
            Warning(string.Format(CultureInfo.InvariantCulture, format, args));
        }

        [ConditionalAttribute("TRACE")]
        public static void Warning(string message)
        {
            var sender = Assembly.GetCallingAssembly();
            if (traceLevel >= TraceLevel.Warning)
                TraceLine(TraceLevel.Warning, message, sender);
        }

        [ConditionalAttribute("TRACE")]
        public static void Debug(string format, params object[] args)
        {
            Debug(string.Format(CultureInfo.InvariantCulture, format, args));
        }

        [ConditionalAttribute("TRACE")]
        public static void Debug(string message)
        {
            var sender = Assembly.GetCallingAssembly();
            if (traceLevel >= TraceLevel.Verbose)
                TraceLine(TraceLevel.Verbose, message, sender);
        }

        private static void TraceLine(TraceLevel level, string message, Assembly sender)
        {
            string prefix = null;

            switch (level)
            {
                case TraceLevel.Error: prefix = "[ERROR]"; break;
                case TraceLevel.Warning: prefix = "[WARNING]"; break;
                case TraceLevel.Info: prefix = "[INFO]"; break;
                case TraceLevel.Verbose: prefix = "[DEBUG]"; break;
                default: prefix = "[DEFAULT]"; break;
            }

            // Assembly info
            string assemblyPrefix = null;
            if (sender != null)
                assemblyPrefix = sender.GetName().Name;

            // Method info
            string methodPrefix = null;
            try
            {
                var stackTrace = new StackTrace(true);

                if (stackTrace != null)
                {
                    var stackFrame = stackTrace.GetFrame(2); // 0 = TraceLine(), 1 = Info(),Debug(),Warning(),Error()
                    if (stackFrame != null)
                    {
                        methodPrefix = stackFrame.GetMethod().Name;
                    }
                }
            }
            catch
            {
                // don't care 
            }

            string finalMessage = string.Format(
                CultureInfo.InvariantCulture,
                "{0}: [{1}|{2}] {3} {4}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff",
                                            CultureInfo.InvariantCulture), assemblyPrefix, methodPrefix, prefix, message);


            Trace.WriteLine(finalMessage);

        }
    }

}
